﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml;
using Templates.SharpParts.Shells;

namespace Templates.Serialization
{
	public interface IT4Serializer
	{
		string Serialize<T>(T obj);
		object Deserialize(string str, string type);
		string Type { get; }
	}

	public class T4Serializer : IT4Serializer
	{
		public virtual string Serialize<T>(T obj)
		{
			using (MemoryStream memoryStream = new MemoryStream())
			{
				using (StreamReader reader = new StreamReader(memoryStream))
				{
					var type = obj.GetType();
					DataContractJsonSerializer serializer = new DataContractJsonSerializer(type);
					serializer.WriteObject(memoryStream, obj);
					memoryStream.Position = 0;
					var result = reader.ReadToEnd();

					UpdateTypeValue(type);

					return result;
				}
			}
		}

		public virtual object Deserialize(string str, string type)
		{
			using (Stream stream = new MemoryStream())
			{
				byte[] data = System.Text.Encoding.UTF8.GetBytes(str);
				stream.Write(data, 0, data.Length);
				stream.Position = 0;
				DataContractJsonSerializer deserializer = new DataContractJsonSerializer(CreateType(type));
				return deserializer.ReadObject(stream);
			}
		}

		public virtual string Type { get; private set; }

		#region protected

		protected string TypeFormat = "{1}{2}{0}";
		protected string TypeFormatDivider = ":#";

		protected virtual void UpdateTypeValue(Type type)
		{
			Type = string.Format(TypeFormat, type.Namespace, type.Name, TypeFormatDivider);
		}

		protected virtual Type CreateType(string type)
		{
			string[] ar = type.Split(new string[] { TypeFormatDivider }, StringSplitOptions.RemoveEmptyEntries);

			if (ar.Length != 2)
				throw new ApplicationException("Incorrect Type Format Or Value \"" + type + "\"");

			var result = System.Type.GetType(string.Format("{0}.{1}", ar[1], ar[0]));

			UpdateTypeValue(result);

			return result;
		}

		#endregion

	}
}
